/*
 * Copyright (c) 2008 niels
 *
 * This file is part of BlueRemote.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with BlueRemote; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 */

#import "appController.h"
#import "BlueRCServerInterface.h"

@implementation appController

/*************************************************************************
*
* init and teardown app
*
*************************************************************************/

- init
{
    self = [super init];
    myBluetoothInterface = [[BlueRCServerInterface alloc] init];
	
    // Registers the callbacks for connection and for disconnection:
    [myBluetoothInterface registerForTermination:self action:@selector(rcHandleRemoteDisconnection)];
    [myBluetoothInterface registerForNewConnection:self action:@selector(rcHandleRemoteConnection)];
    
    // gets the local device name:
    localDeviceName = [myBluetoothInterface localDeviceName];
    [localDeviceName retain];
	
	serverStatus = rcStatus_Offline;
	
	// register with Growl
	[GrowlApplicationBridge setGrowlDelegate:self];
	
    return self;
}

- (void) awakeFromNib{
    
    //Create the NSStatusBar and set its length
	statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
    
    //Allocates and loads the images into the application which will be used for our NSStatusItem
    statusImage = [NSImage imageNamed:@"status_unconnected.png"];
    
    //Sets the images in our NSStatusItem
    [statusItem setImage:statusImage];
	//[statusItem setAlternateImage:statusHighlightImage];
    
    //Tells the NSStatusItem what menu to load
    [statusItem setMenu:statusMenu];
    //Sets the tooptip for our item
	[statusItem setToolTip:@"BlueRC - server not started"];
	
    //Enables highlighting
	[statusItem setHighlightMode:YES];
}

- (void) dealloc 
{
	[localDeviceName release];
	[statusImage release];
	[statusItem release];
	[statusMenu release];
	[myBluetoothInterface release];
    [super dealloc];
}



/*************************************************************************
*
* UI handlers
*
*************************************************************************/

- (IBAction)exitApp:(id)sender
{
	[myBluetoothInterface stopProvidingRCServices];
    [myBluetoothInterface disconnectFromClient];
	[self showGrowlNotification:@"app is exiting normally"];
	exit(0);
}

- (IBAction)rcActionToggleServer:(id)sender
{
	switch (serverStatus) {
	case rcStatus_Offline:
		[self rcStartServer];
		break;
	case rcStatus_Waiting:
		[myBluetoothInterface stopProvidingRCServices];
		[self setRCStatus:rcStatus_Offline];
		NSLog(@"server shut down");
		break;
	case rcStatus_Connected:
		[myBluetoothInterface disconnectFromClient];
		[self setRCStatus:rcStatus_Waiting];
		break;
	}
}

- (void)setMenuItemLabel:(NSString*)newLabel
{
	NSMenu *myMenu = [statusItem menu];
	NSArray *menuItems = [myMenu itemArray];
	NSMenuItem *firstItem = [menuItems objectAtIndex:2];
	[firstItem setTitle:newLabel];
}

/*
 *		This dictionary gives Growl the complete list of notifications this application will ever send,
 *		and it also specifies which notifications should be enabled by default. 
 */	
- (NSDictionary *) registrationDictionaryForGrowl {
	NSArray *notifications = [NSArray arrayWithObjects:
		@"BlueRC Status Notification",
		nil];
	
	NSDictionary *regDict = [NSDictionary dictionaryWithObjectsAndKeys:
		@"BlueRC", GROWL_APP_NAME,
		notifications, GROWL_NOTIFICATIONS_ALL,
		notifications, GROWL_NOTIFICATIONS_DEFAULT,
		nil];
	
	return regDict;
}

/*
 *		Sending a Growl notification.
 *		Priority ranges from -2 (low) to +2 (emergency) 
 *		Sticky makes the notification stay on screen until clicked.
 *		clickContext will be passed back to the delegate method if the
 *		user clicks the notification.  If can be anything you want.
 */
- (void)showGrowlNotification:(NSString*)message
{
	[GrowlApplicationBridge notifyWithTitle:@"BlueRC"
								description:message
						   notificationName:@"BlueRC Status Notification"
								   iconData: nil
								   priority:0
								   isSticky:NO
							   clickContext:message];
	
}

/*************************************************************************
*
* Bluetooth handlers
*
*************************************************************************/

// starting the server
- (void)rcStartServer
{
    // Publishes the services:
    if ([myBluetoothInterface publishService])
    {
        [self setRCStatus:rcStatus_Waiting];
		NSLog(@"server started");
    }
	else 
		NSLog(@"failed to publish serice");
}

// end of server start-up sequence
- (void)connectionFollowUp:(BOOL)success
{
	// publish one service only, since only one remote control is to be used
    [myBluetoothInterface stopProvidingRCServices];
	
    if (success == FALSE)
    {
		NSLog(@"server start-up sequence failed");
		[self setRCStatus:rcStatus_Offline];
        NSBeep();
    }
    else
    {
        // connection successfull, register for new data
        [myBluetoothInterface registerForNewData:self action:@selector(rcHandleNewData:)];
        
        [self setRCStatus:rcStatus_Connected];
		NSLog(@"server connected");
    }
}

- (void)rcHandleRemoteConnection
{	
    [self connectionFollowUp:TRUE];
}

- (void)rcHandleRemoteDisconnection
{
    NSLog(@"remote disconnection");
	
    [myBluetoothInterface disconnectFromClient];
	[myBluetoothInterface registerForNewData:nil action:nil];
	[self setRCStatus:rcStatus_Offline];
	
	// server is offline after remote disconnect, restart it
	[self rcStartServer];
	[self setRCStatus:rcStatus_Waiting];
}

- (void)rcHandleNewData:(NSData*)dataObject
{
	NSString *receivedStr = [NSString stringWithCString:[dataObject bytes] length:[dataObject length]];
	[self postGlobalKeyEvent:receivedStr];
}

- (void)setRCStatus:(rcStatus)newStatus
{
	switch (newStatus)
	{
		case rcStatus_Offline:
			{
				// rename menu item
				[self setMenuItemLabel:@"start server"];
				
				// set appropriate status image
				statusImage = [NSImage imageNamed:@"status_unconnected.png"];
				[statusItem setImage:statusImage];		
				
				// change tool tip
				[statusItem setToolTip:@"BlueRC - server not started"];
				
				// set server status
				serverStatus = rcStatus_Offline;
				[self showGrowlNotification:@"BlueRC is offline"];
			}
			break;
		case rcStatus_Waiting:
			{
				// rename menu item
				[self setMenuItemLabel:@"stop server"];
				
				// set appropriate status image
				statusImage = [NSImage imageNamed:@"status_waiting.png"];
				[statusItem setImage:statusImage];		
				
				// change tool tip
				[statusItem setToolTip:@"BlueRC - waiting for connections"];
				
				// set server status
				serverStatus = rcStatus_Waiting;
				[self showGrowlNotification:@"BlueRC is waiting for connections"];
			}
			break;
		case rcStatus_Connected:
			{
				// rename menu item
				[self setMenuItemLabel:@"disconnect client"];
				
				// set appropriate status image
				statusImage = [NSImage imageNamed:@"status_connected.png"];
				[statusItem setImage:statusImage];		
				
				// change tool tip
				NSString *basicString = @"BlueRC - connected to ";
				NSString *remoteDeviceName = [myBluetoothInterface remoteDeviceName];
				NSString *toolText = [basicString stringByAppendingString:remoteDeviceName];
				[statusItem setToolTip:toolText];
				
				// set server status
				serverStatus = rcStatus_Connected;
				[self showGrowlNotification:toolText];
			}
			break;
		default:
			{
				NSMutableString *logStr = @"appController::setRCStatus: trying to set unknown status";
				[logStr appendString:[[NSNumber numberWithInt:newStatus] stringValue]];;
				NSLog(logStr);
			}
			break;
	}
	
}


/*************************************************************************
*
* posting the global key event
*
*************************************************************************/

- (void)postGlobalKeyEvent:(NSString*)theString
{
	// see this link for possible key values:
	//     http://developer.apple.com/documentation/mac/Toolbox/Toolbox-40.html
	
	int keyValue = 31;
	
	// right
	if ([theString isEqualToString:@"1"])
		keyValue = 49;
	// left
	else if ([theString isEqualToString:@"2"])
		keyValue = 51;
	// up
	else if ([theString isEqualToString:@"3"])
		keyValue = 13;
	// down
	else if ([theString isEqualToString:@"4"])
		keyValue = 11;
	// middle
	else if ([theString isEqualToString: @"5"])
		keyValue = 49;
	// end
	//	else if ([theString isEqualToString:@"0"])
	//		keyValue = 29;
	
	CGEnableEventStateCombining(FALSE);
	CGPostKeyboardEvent(0, keyValue, true);
	CGPostKeyboardEvent(0, keyValue, false);
}


@end
